/*
 * Copyright 2014 Will Dignazio
 */
#include <stdarg.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <socketio.h>
#include "internal.h"

/*
 * URI Format:
 * [scheme]://[host]/[namespace]/[protocol version]/[transport id]/[session id]/( '?' [query] )
 */

enum
{
	MAXLEN	 = 15,
};

/*
 * Hack way to pass session plus callback
 */
typedef struct RequestTuple RequestTuple;
struct RequestTuple
{
	SocketSession	*session;
	int		(*callback)(SocketData *sdata, SocketSession *session);
};

SocketData*
allocdata(char *cdata, size_t bsize, size_t nelem)
{
	SocketData *sdata;
	char *sdatabuf;

	sdata = NULL;
	sdatabuf = NULL;

	sdata = malloc(sizeof(*sdata));
	if(sdata == NULL)
		goto fail;

	sdatabuf = malloc(bsize * nelem); // We need to copy the data
	if(sdatabuf == NULL)
		goto fail;

	memcpy(sdatabuf, cdata, bsize*nelem);
	sdata->cdata = sdatabuf;
	sdata->bsize = bsize;
	sdata->nelem = nelem;

	return sdata;

fail:
	freedata(sdata);
	return sdata;
}

void
freedata(SocketData *sdata)
{
	if(sdata == NULL)
		return;

	if(sdata->vdata != NULL)
		free(sdata->vdata);

	free(sdata);
}

static size_t
curlwrapper(char *ptr, size_t size, size_t nmb, void *data)
{
	SocketData *sdata;
	RequestTuple *rt;
	int ret;

	sdata = NULL;
	rt = data;

	if(nmb == 0)
		goto fail;

	sdata = allocdata(ptr, size, nmb);
	if(sdata == NULL)
		goto fail;

	ret = rt->callback(sdata, rt->session);
	if(ret != 0)
		goto fail;
	
fail:
	freedata(sdata);
	// TODO free sdata if it goes awry
	return nmb;
}

int
postrequest(SocketRequest *req, SocketSession *session, SocketCallback callback)
{
	CURL *curl;
	CURLcode res;
	int httpcode;
	struct curl_httppost *formpost;
	struct curl_httppost *lastptr;
	RequestTuple *rt;
	size_t reqstrlen;
	char *reqstr;
	char protostr[MAXLEN];
	char tidstr[MAXLEN];
	char sidstr[MAXLEN];
	int ret;

	protostr[0] = '\0';
	tidstr[0] = '\0';
	sidstr[0] = '\0';

	httpcode = 0;
	reqstr = NULL;
	reqstrlen = 0;
	formpost = NULL;
	lastptr = NULL;
	ret = -1;
	rt = NULL;

	curl = curl_easy_init();
	if(curl == NULL) {
		ret = -1;
		goto fail;
	}

	rt = malloc(sizeof(*rt));
	if(rt == NULL)
		goto fail;

	rt->session = session;
	rt->callback = callback;

	/* Assemble request string */
	sprintf(protostr, "%d", req->proto);

	if(req->tid != 0)
		sprintf(tidstr, "%d/", req->tid);

	if(req->sid != 0)
		sprintf(sidstr, "%d/", req->sid);

	reqstrlen += strlen(req->scheme);
	reqstrlen += strlen(req->host);
	reqstrlen += strlen(req->ns);
	reqstrlen += strlen(protostr);
	reqstrlen += strlen(tidstr);
	reqstrlen += strlen(sidstr);
	reqstrlen += strlen(req->query);
	reqstrlen += 6; // For intermediate characters

	reqstr = malloc(reqstrlen+1); // 1 extra for null terminator
	snprintf(reqstr, reqstrlen, "%s://%s/%s/%s/%s%s?%s",
		 req->scheme,
		 req->host,
		 req->ns,
		 protostr,
		 tidstr,
		 sidstr,
		 req->query);

	curl_easy_setopt(curl, CURLOPT_URL, reqstr);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlwrapper);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, rt);

	res = curl_easy_perform(curl);
	if(res != CURLE_OK) {
		ret = -1;
		goto fail;
	}

	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode);
	if(httpcode != 200)
		ret = res;
	else
		ret = httpcode;

fail:
	if(rt != NULL)
		free(rt);

	curl_easy_cleanup(curl);
	if(reqstr != NULL)
		free(reqstr);

	return ret;
}

SocketRequest*
allocrequest(char *scheme,
	     char *host,
	     char *ns,
	     int proto,
	     int tid,
	     int sid,
	     char *query)
{
	SocketRequest *req;

	req = calloc(1, sizeof(*req));
	if(req == NULL)
		return NULL;

	req->scheme = malloc(strlen(scheme));
	if(req->scheme == NULL)
		goto fail;

	req->host = malloc(strlen(host));
	if(req->host == NULL)
		goto fail;

	req->ns = malloc(strlen(ns));
	if(req->ns == NULL)
		goto fail;

	req->query = malloc(strlen(query));
	if(req->query == NULL)
		goto fail;

	strcpy(req->scheme, scheme);
	strcpy(req->host, host);
	strcpy(req->ns, ns);
	strcpy(req->query, query);

	req->proto = proto;
	req->tid = tid;
	req->sid = sid;

	return req;
fail:
	if(req != NULL) {
		if(req->scheme != NULL)
			free(req->scheme);
		if(req->host != NULL)
			free(req->scheme);
		if(req->query != NULL)
			free(req->query);
		free(req);
	}
	return NULL;
}

void
freerequest(SocketRequest *req)
{
	if(req == NULL)
		return;

	if(req->scheme != NULL)
		free(req->scheme);
	if(req->host != NULL)
		free(req->host);
	if(req->ns != NULL)
		free(req->ns);
	if(req->query != NULL)
		free(req->query);

	free(req);
}
